#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_err.h"
#include "i2c_custom.h"
#include "max30102.h"
#include "spo2_algorithm.h"
#include "heartRate.h"
#include "max30102_app.h"
#include "esp_timer.h"
#include "esp_now_hal.h"

static const char *TAG = "max30102_app";

/* sdadsa */
uint32_t irBuffer[100];  // infrared LED sensor data
uint32_t redBuffer[100]; // red LED sensor data
int32_t bufferLength;    // data length
int32_t spo2;            // SPO2 value
int8_t validSPO2;        // indicator to show if the SPO2 calculation is valid
int32_t heartRate;       // heart rate value
int8_t validHeartRate;   // indicator to show if the heart rate calculation is valid
/*dsaad s*/

/* dsadsa */
#define RATE_SIZE 4       // Increase this for more averaging. 4 is good.
int32_t rates[RATE_SIZE]; // Array of heart rates
int32_t rateSpot = 0;
int64_t lastBeat = 0; // Time at which the last beat occurred
float beatsPerMinute;
int beatAvg;
/* dsadsa */

static max30102_app max30102_pannel = {0, 0, 0, 0};
static uint16_t count_time = 0;

/**
 * @brief max30102_app_status_set
 *
 */
void max30102_app_status_set(void)
{
    max30102_pannel.read_status = 1;
}

/**
 * @brief max30102_app_status_get
 *
 * @return bool
 */
bool max30102_app_status_get(void)
{
    return max30102_pannel.read_status;
}

/**
 * @brief max30102_get_heart_rate
 *
 * @return uint8_t
 */
uint8_t max30102_get_heart_rate(void)
{
    return max30102_pannel.heart_rate_date;
}

/**
 * @brief max30102_get_spo2
 *
 * @return uint8_t
 */
uint8_t max30102_get_spo2(void)
{
    return max30102_pannel.spo2_date;
}

/**
 * @brief max30102_task
 *
 */
void max30102_task(void *arg)
{
    vTaskDelay(pdMS_TO_TICKS(50));                /* delay 50ms */
    ESP_ERROR_CHECK(max30102_int_gpio_init());    /* init max30102 int pin */
    ESP_ERROR_CHECK(max30102_reset());            /* reset max30102 */
    vTaskDelay(pdMS_TO_TICKS(1));                 /* delay 1ms */
    ESP_ERROR_CHECK(max30102_clear_status());     /* clear max30102 status */
    ESP_ERROR_CHECK(max30102_init());             /* init max30102 reg */
    vTaskDelay(pdMS_TO_TICKS(5));                 /* delay 5ms */
    ESP_LOGI(TAG, "MAX30102 init successfully."); /* printf init log */

    uint32_t red, ir;
    bufferLength = 100; // buffer length of 100 stores 4 seconds of samples running at 25sps
    // read the first 100 samples, and determine the signal range
    for (uint8_t i = 0; i < bufferLength; i++)
    {
        max30102_read_fifo(&red, &ir);
        redBuffer[i] = red;
        irBuffer[i] = ir;
        // particleSensor.nextSample();
        // We're finished with this sample so move to next sample
#if MAX30102_HAL_DEBUG
        printf("red[%d]:%ld   ir[%d]:%ld\r\n", i, redBuffer[i], i, irBuffer[i]);
#endif
    }
    maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

    while (1)
    {
        if ((gpio_get_level(MAX30102_INT_PIN) == 0) && max30102_pannel.read_status == 1) /* check max30102 read ui obj */
        {
            /* check cal count start */
            count_time++;
            if (count_time == 20) /* read 20 per */
            {
                count_time = 0;
                max30102_pannel.read_status = 0;
                /* read ok send data to master start */
                char str_max30102_buff[18];
                sprintf(str_max30102_buff, "SOP2:%d,HR:%d", max30102_get_heart_rate(), max30102_get_spo2());
                esp_now_send_data(str_max30102_buff);
                /* read ok send data to master end */
            }
            /* check cal count end */

            // dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
            for (uint8_t i = 25; i < 100; i++)
            {
                redBuffer[i - 25] = redBuffer[i];
                irBuffer[i - 25] = irBuffer[i];
            } // take 25 sets of samples before calculating the heart rate.
            for (uint8_t i = 75; i < 100; i++)
            {
                vTaskDelay(pdMS_TO_TICKS(10));
                max30102_read_fifo(&red, &ir);
                /* spo2 proc start */
                redBuffer[i] = red;
                irBuffer[i] = ir;

                /* check finger start */
                if (ir < 60000)
                {
                    red = 0;
                    ir = 0;
                }
                /* check finger end */

                if (validSPO2 == 1 && spo2 > 75) /* SPO2 is ok */
                {
                    max30102_pannel.spo2_date = spo2;
#if MAX30102_HAL_DEBUG
                    printf("red[%d]:%ld   ir[%d]:%ld    HR=%ld     HRvalid=%d    SPO2=%ld SPO2Valid=%d\r\n", i, redBuffer[i], i, irBuffer[i], heartRate, validHeartRate, spo2, validSPO2);
#endif
                }
                /* spo2 proc end */

                /* heart rate proc start */
                if (checkForBeat(ir) == true) /* heart is ok */
                {
                    // We sensed a beat!
                    int64_t delta = (esp_log_timestamp()) - lastBeat;
                    lastBeat = (esp_log_timestamp());
                    printf("delta:%lld  lastBeat:%lld\r\n", delta, lastBeat);
                    beatsPerMinute = 60 / (delta / 1000.0);
                    if (beatsPerMinute < 160 && beatsPerMinute > 70)
                    {
                        rates[rateSpot++] = (uint32_t)beatsPerMinute; // Store this reading in the array
                        rateSpot %= RATE_SIZE;                        // Wrap variable
                        // Take average of readings
                        beatAvg = 0;
                        for (uint8_t x = 0; x < RATE_SIZE; x++)
                            beatAvg += rates[x];
                        beatAvg /= RATE_SIZE;

                        max30102_pannel.heart_rate_date = beatsPerMinute;
                        printf("IR:%ld   BPM=%.2f    Avg BPM=%d\r\n", ir, beatsPerMinute, beatAvg);
                    }
                }
                /* heart rate proc end */
            }
            // After gathering 25 new samples recalculate HR and SP02
            maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

/* https://github.com/Jasoji/stm32-max30102/tree/master */

/* https://circuitdigest.com/microcontroller-projects/how-max30102-pulse-oximeter-and-heart-rate-sensor-works-and-how-to-interface-with-arduino */

/*
SpO2 = -45.060*R*R + 30.354 *R + 94.845
R = ( ACred/DCred )/( ACired/DCired )
https://www.stg-maximintegrated.com/cn/design/technical-documents/app-notes/6/6845.html
*/